<?php

/*

Desktopd Colors
Copyright (C) 2016  Desktopd Developer(s)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

https://www.gnu.org/licenses/agpl.html

*/


namespace Desktopd\Colors;


class sRGB {
    protected $r = 1;
    protected $g = 1;
    protected $b = 1;
    
    public static function fromXYZ(XYZ $XYZ, $strict = false) {
        $linearRGB = (new MatrixTransform(
            array( 3.2406, -1.5372, -0.4986),
            array(-0.9689,  1.8758,  0.0415),
            array( 0.0557, -0.2040,  1.0570)
        ))->transform(array(
            $XYZ->getX(),
            $XYZ->getY(),
            $XYZ->getZ()
        ));
        
        $min = min($linearRGB);
        if ($min < 0) {
            if ($strict) {
                throw new \OutOfRangeException('Given color is out of sRGB gamut');
            }
            
            $bias = 0 - $min;
            $linearRGB = array_map(function ($c) use($bias) {
                return $c + $bias;
            }, $linearRGB);
            
            $ratio = $XYZ->getY() / (new MatrixTransform(
                array(0.4124, 0.3576, 0.1805),
                array(0.2126, 0.7152, 0.0722),
                array(0.0193, 0.1192, 0.9502)
            ))->transform($linearRGB)[1];
            
            $linearRGB = array_map(function ($c) use($ratio) {
                return $c * $ratio;
            }, $linearRGB);
        }
        
        list($r, $g, $b) = array_map(function ($c) {
            if ($c > 0.0031308) {
                return 1.055 * pow($c, 1 / 2.4) - 0.055;
            } else {
                return 12.92 * $c;
            }
        }, $linearRGB);
        
        return new self($r, $g, $b, $strict);
    }
    
    public function __construct($r, $g, $b, $strict = false) {
        list(
            $this->r,
            $this->g,
            $this->b
        ) = @array_map(function($c) use($strict) {
            if (!is_numeric($c)) {
                throw new \InvalidArgumentException(print_r($c, true) . ' is not numeric');
            }
            $c = (float) $c;
            
            // is this right?
            ///*
            if ($strict) {
                if ($c > 1 || $c < 0 ) {
                    throw new \OutOfRangeException('Each value must be in [0, 1]');
                }
            }
            //*/
            
            //return $c;
            return max(0, min(1, $c));
        }, array($r, $g, $b));
        
    }
    
    public function toXYZ() {
        $linearRGB = array_map(function($c) {
            if ($c > 0.04045) {
                return pow(($c + 0.055) / 1.055, 2.4);
            } else {
                return $c / 12.92;
            }
        }, array($this->r, $this->g, $this->b));
        
        list($x, $y, $z) = (new MatrixTransform(
            array(0.4124, 0.3576, 0.1805),
            array(0.2126, 0.7152, 0.0722),
            array(0.0193, 0.1192, 0.9502)
        ))->transform($linearRGB);
        
        return new XYZ($x, $y, $z);
    }
    
    public function __toString() {
        $rgb = array_map (function ($c) {
            return sprintf ('%.3f', $c * 100);
        }, array($this->r, $this->g, $this->b));
        return 'rgb(' . implode ('%,', $rgb) . '%)';
    }
    
    public function getR() {
        return $this->r;
    }
    
    public function getG() {
        return $this->g;
    }
    
    public function getB() {
        return $this->b;
    }
}


// vim: ts=4 et ai

